package player;

import java.nio.ByteBuffer;

import org.apache.log4j.Logger;

import common.StopWatch;

import main.Game;
import netengine.NetSocketHandler;
import packet.Packet;
import packet.PacketConstant;
import packet.PacketFactory;

public class Player 
{
	static Logger logger = Logger.getLogger(Game.class);
	
	public Player(NetSocketHandler socketHandler)
	{
		netSocketHandler     = socketHandler;
		playerManager        = null;
		runtimeID            = PlayerManager.generateRuntimeID();
		maxIdleTimeInMillis  = 60 * 5 * 1000;
		lastTickTimeInMillis = 0;
	}
	 
	public void processMessage(long nowInMillis, int moduleFlag)
	{
		boolean isIllegalData = false;
		boolean isIdleTick    = true;
		
		if (netSocketHandler.isException() || netSocketHandler.isClosed())
		{
			close();
			return;
		}
		
		if (netSocketHandler.readableDataSize() > 1024 * 6)
		{
			logger.info("the player has too much pending data in read buffer.");
			close();
			return;
		}
			
		  
		for (int i = 0; i < 3; i++)
		{
			byte[] data = netSocketHandler.readData();
			
			if (data == null)
			{
				break;
			}
			
			Packet packet = PacketFactory.getInstance().createPacket(data);
			if (packet == null)
			{
				isIllegalData = true;
				break;
			}
			 
			logger.debug("ready to execute packet " + packet.getPacketDesc());
			StopWatch stopWatch = new StopWatch();
			stopWatch.start();
			int result = packet.execute(this, PacketConstant.PACKET_ALLOWABLE_LOGIN);
			stopWatch.stop();
			String log = String.format("Execute the packet[%s] with the result code: %d, spend time[%d usecs] to finish for the player.",
									   packet.getPacketName(), result, stopWatch.elapseTime() / 1000);
			logger.info(log);
			
			if (result != 0)
			{
				isIllegalData = true;
				break;
			}
			
			isIdleTick = false;
		}
		
		if (isIllegalData)
		{
			logger.error("receive illegal data from player, kick off the player.");
			close();
			return;
		}
		
		if (isIdleTick)
		{
			if (lastTickTimeInMillis != 0)
			{
				maxIdleTimeInMillis -= (nowInMillis - lastTickTimeInMillis);
			}
			
			lastTickTimeInMillis = nowInMillis;
			
			if (maxIdleTimeInMillis <= 0)
			{ 
				logger.warn("the player idle time too long, kick off the player.");
				close();
				return;
			} 
		} 
		else
		{ 
			maxIdleTimeInMillis = 60 * 5 * 1000;
		}
		
		lastTickTimeInMillis = nowInMillis;
	}
	
	public boolean sendPacket(Packet packet)
	{
		int packetSize = packet.getPacketSize();
		ByteBuffer byteBuffer = ByteBuffer.allocate(packetSize + 4);
		
		try
		{
			byteBuffer.position(4); 
			int writtenByte = packet.writeToBuffer(byteBuffer);
			byteBuffer.putInt(0, writtenByte);
			byteBuffer.position(0);
			byteBuffer.limit(writtenByte + 4); 
			netSocketHandler.writeData(byteBuffer);
			
			logger.debug("send packet to palyer " + packet.getPacketDesc());
		}
		catch (Exception exception)
		{ 
			logger.error("Exception: ", exception);
			close();
		}
		
		return true;
	}
	
	public void close()
	{
		netSocketHandler.close();
		
		if (playerManager != null)
		{
			playerManager.removePlayer(this);
		}
	}
	
	public int getRuntimeID()
	{
		return runtimeID;
	}
	
	public synchronized void setPlayerManager(PlayerManager manager)
	{
		if (playerManager != null)
		{
			playerManager.removePlayer(this);
		}
		
		playerManager = manager;
	}
	
	public PlayerManager getPlayerManager()
	{
		return playerManager;
	}
	
	private long maxIdleTimeInMillis;
	private long lastTickTimeInMillis;
	private int runtimeID;
	private PlayerManager playerManager;
	protected NetSocketHandler netSocketHandler; 
}
